const mongoose = require('mongoose')
const bcrypt = require('bcryptjs')
const Schema = mongoose.Schema
const Mixed = Schema.Types.Mixed
const SALT_WORK_FACTOR = 10
const MAX_LOGIN_ATTEMPS = 5
const LOCK_TIME = 2 * 60 * 60 * 1000

const userSchema = new Schema({
	// user admin superAdminmon
	role: {
		type: String,
		default: 'user'
	},
	username: {
		unique: true,
		required: true,
		type: String
	},
	password: {
		unique: true,
		type: String
	},
	email: {
		unique: true,
		required: true,
		type: String
	},
	loginAttempts: {
		type: Number,
		required: true,
		default: 0
	},
	lockUtil: Number,
	meta: {
		createdAt: {
			type: Date,
			default: Date.now()
		},
		updatedAt: {
			type: Date,
			default: Date.now()
		}
	}
})
// 虚拟字段，不会被保存数据库，是个计算属性
userSchema.virtual('isLock').get(function () {
	return !!(this.lockUtil && this.lockUtil > Date.now())
})

userSchema.pre('save', function (next) {
	if (this.isNew) {
		this.meta.createdAt = this.meta.updatedAt = Date.now()
	} else {
		this.meta.updatedAt = Date.now()
	}
	next()
})
userSchema.pre('save', function (next) {
	if (!this.isModified('password')) return next()
	bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
		if (err) return next(err)
		bcrypt.hash(this.password, salt, (error, hash) => {
			if (error) return next(error)
			this.password = hash
			next()
		})
	})
})

userSchema.methods = {
	comparePassword: (_password, password) => {
		return new Promise((res, rej) => {
			bcrypt.compare(_password, password, (err, isMatch) => {
				if (!err) res(isMatch)
				else rej(err)
			})
		})
	},

	incLoginAttepts: (user) => {
		return new Promise((res, rej) => {
			if (this.lockUtil && this.lockUtil < Date.now()) {
				this.update({
					$set: {
						loginAttempts: 1,
					},
					$unset: {
						lockUntil: 1
					}
				}, (err) => {
					if (!err) res(true)
					else rej(err)
				})
			} else {
				let updates = {
					$inc: {
						loginAttempts: 1
					}
				}

				if (this.loginAttempts + 1 >= MAX_LOGIN_ATTEMPS && !this.isLock) {
					update.$set = {
						lockUntil: Date.now() + LOCK_TIME
					}
				}

				this.update(updates, (err) => {
					if (!err) res(true)
					else rej(err)
				})
			}
		})
	}

}

mongoose.model('User', userSchema)
